
package com.hockeo.client.model;

import java.util.Date;
import java.util.List;

import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.google.gwt.user.client.rpc.IsSerializable;

/**
 * Basic data transfer object. This can be used as a general purpose object either on the
 * client side only or when transferring server side data.
 *
 * @version $Id$
 * @author jjanke
 */
@SuppressWarnings( "serial" )
public class JSData extends BaseTreeModel implements IsSerializable
{
  public static final String PARAM_SERVER_CLASS    = "js_class";
  public static final String PARAM_ID              = "js_id";
  public static final String PARAM_REMOTE_CHILDREN = "js_has_remote_children";
  public static final String PARAM_CHANGE_TYPE     = "js_change_type";

  /**
   * Used when JS model items are changed and sent back to the server to identify whether
   * an item is modified, deleted or unchanged.
   */
  public enum ChangeType
  {
    UNCHANGED, NEW, MODIFIED, DELETED;
  }

  /** No-arg constructor for GWT/RPC. */
  public JSData()
  {}

  /**
   * Creates a new JSData object.
   *
   * @param strServerClass the name of the corresponding server class
   */
  public JSData( String strServerClass )
  {
    set( PARAM_SERVER_CLASS, strServerClass );
  }

  /**
   * Creates a new JSData object adding the list of items as children.
   *
   * @param listChildren the child items to be added immediately
   */
  public JSData( List<JSData> listChildren )
  {
    addAll( listChildren );
  }

  /**
   * Returns the fully qualified name of the server side class this object is based on.
   */
  public String getServerClass()
  {
    return get( PARAM_SERVER_CLASS );
  }

  public void setChangeType( ChangeType ct )
  {
    set( PARAM_CHANGE_TYPE, ct.name() );
  }

  /**
   * Indicates whether this object has been modified, deleted or is unchanged.
   */
  public ChangeType getChangeType()
  {
    String strChangeType = get( PARAM_CHANGE_TYPE );

    return strChangeType != null ? ChangeType.valueOf( strChangeType ) : ChangeType.UNCHANGED;
  }

  /**
   * Returns the unique item ID as string (the value of the "id" property).
   */
  public String getId()
  {
    return String.valueOf( get( PARAM_ID ) );
  }

  public void addAll( List<JSData> list )
  {
    for ( JSData data : list )
      add( data );
  }

  public String getString( String strKey )
  {
    return (String) get( strKey );
  }

  public Date getDate( String strKey )
  {
    return (Date) get( strKey );
  }

  public boolean getBoolean( String strKey )
  {
    Boolean b = get( strKey );

    return b!= null ? b.booleanValue() : false;
  }

  public int getInt( String strKey )
  {
    Integer n = get( strKey );

    return n != null ? n.intValue() : 0;
  }

  public double getDouble( String strKey )
  {
    Double d = get( strKey );

    return d != null ? d.doubleValue() : 0.0;
  }

  /**
   * Checks if there is a property with the given name that is not null.
   *
   * @param strName the property to be checked
   * @return <code>true</code> if the property is set and has a non-<code>null</code>
   *         value
   */
  public boolean isPropertySet( String strName )
  {
    return get( strName ) != null;
  }

  /**
   * Indicates whether this object has currently some child nodes attached or not.
   */
  public boolean hasChildren()
  {
    return getChildCount() > 0;
  }

  /**
   * Indicates whether this object has remote children (to be loaded via RPC calls) or
   * not.
   */
  public boolean hasRemoteChildren()
  {
    return getBoolean( PARAM_REMOTE_CHILDREN );
  }

  /**
   * Renames a property.
   *
   * @param strOldName the name of the property to be renamed
   * @param strNewName the new name of the property
   */
  public void renameProperty( String strOldName, String strNewName )
  {
    Object val = remove( strOldName );
    set( strNewName, val );
  }

  /**
   * Renames all properties whose names start with the given prefix. The prefix is
   * replaced by the new one.
   *
   * @param strOldName the name of the property to be renamed
   * @param strNewName the new name of the property
   */
  public void renamePropertiesWithPrefix( String strOldPrefix, String strNewPrefix )
  {
    for ( String strProperty : getPropertyNames() )
    {
      if ( strProperty.startsWith( strOldPrefix ) )
      {
        String strNewName;

        // If the property name is longer than the given prefix, we have to reuse part of
        // the name in the new name. Otherwise, we only use the new prefix.
        //
        if ( strProperty.length() > strOldPrefix.length() )
        {
          String strSuffix = strProperty.substring( strOldPrefix.length() );

          strNewName = strNewPrefix + strSuffix;
        }
        else
          strNewName = strNewPrefix;

        renameProperty( strProperty, strNewName );
      }
    }
  }

  /**
   * Returns all properties defined for this object in a string object (one property per
   * line).
   */
  public String dump()
  {
    StringBuilder sb = new StringBuilder();

    for ( String strProp : getProperties().keySet() )
    {
      if ( sb.length() > 0 )
        sb.append( ",\n" );

      sb.append( strProp + ": " + get( strProp ) );
    }

    return sb.toString();
  }
}
